/**
 * Trabajo de Ingeniería de Software 2
 * Año 2008
 * Sistema de Reserva de Recursos.
 * Desarrolladores: - Marcelo Ferreira.
 *                  - Christian Gómez.
 *                  - Marcelo Rodas.
 */
package controller;

import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import javax.persistence.Query;
import javax.faces.application.FacesMessage;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.faces.FacesException;
import java.util.HashMap;
//import javax.faces.validator.ValidatorException;
import javax.transaction.UserTransaction;
import modelo.Devolucion;
import modelo.Reserva;
import modelo.Ubicacion;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import javax.servlet.http.HttpSession;
import modelo.Usoretiro;
import modelo.Usuario;

/**
 * Clase Controladora de las Operaciones relacionadas a un Uso/Retiro.
 * @author mrodas
 * @version 2.0
 */
public class UsoretiroController implements UsoretiroControllerInterface {
    // Objeto Usoretiro que es manipulado en esta clase.

    private Usoretiro usoretiro = null;

    public void setUsoretiro(Usoretiro usoretiro) {
        this.usoretiro = usoretiro;
    }
    // Conjunto de muchos objetos Uso/Retiros
    private List<Usoretiro> usoretiros = null;
    // Objeto que maneja la Transaccion de Usuarios.
    @Resource
    private UserTransaction utx = null;
    // Fabrica de Entity Manager para manejo de la Persistencia.
    @PersistenceUnit(unitName = "sistemaReservaPU")
    private EntityManagerFactory emf = null;
    // Contiene el ID del Operador.    
    public String idOperador;
    /**
     * Getter de Entity Manager
     * @return emf.createEntityManager()
     */
    public EntityManager getEntityManager() {
        return emf.createEntityManager();
    }
    /*    public int batchSize = 100;
    private int firstItem = 0;*/
    private int itemCount = -1;

    public UsoretiroController(Usoretiro usoretiro){
        this.usoretiro =usoretiro;
    }
    
    /**
     * Constructor Básico de la Clase
     */
    public UsoretiroController() {
        FacesContext context = FacesContext.getCurrentInstance();
        context.getExternalContext().getSessionMap().put("usoretiro_list", this);
    }

    /**
     * Setter de la Devolución Relacionada al Usoretiro.
     * @param devolucionCollection
     */
    public void setDevolucionCollectionOfUsoretiro(Devolucion[] devolucionCollection) {
        List<Devolucion> devolucionCollectionList = Arrays.asList(devolucionCollection);
        usoretiro.setDevolucionCollection(devolucionCollectionList);
    }

    /**
     * Getter de la Devolución Relacionada al Usoretiro.
     * @return Devolucion[]
     */
    public Devolucion[] getDevolucionCollectionOfUsoretiro() {
        Collection<Devolucion> devolucionCollection = usoretiro.getDevolucionCollection();
        if (devolucionCollection == null) {
            return new Devolucion[0];
        }
        return devolucionCollection.toArray(new Devolucion[0]);
    }

    /**
     * Getter del UsoRetiros Disponibles en la base de datos
     * @return SelectItem[]
     */
    public SelectItem[] getUsoretirosAvailableSelectMany() {
        return getUsoretirosAvailable(false);
    }

    /**
     * Getter del UsoRetiros Disponibles en la base de datos con un
     * elemento vacio al principio del vector respuesta.
     * @return SelectItem[]
     */
    public SelectItem[] getUsoretirosAvailableSelectOne() {
        return getUsoretirosAvailable(true);
    }

    /*
     * Función para Construir el SelectItem[]
     */
    private SelectItem[] getUsoretirosAvailable(boolean one) {
        List<Usoretiro> allUsoretiros = getUsoretiros(true);
        int size = one ? allUsoretiros.size() + 1 : allUsoretiros.size();
        SelectItem[] items = new SelectItem[size];
        int i = 0;
        if (one) {
            items[0] = new SelectItem("", "---");
            i++;
        }
        for (Usoretiro x : allUsoretiros) {
            items[i++] = new SelectItem(x, x.toString());
        }
        return items;
    }

    /**
     * Getter del Usoretiro representado
     * @return Usoretiro
     */
    public Usoretiro getUsoretiro() {
        if (usoretiro == null) {
            usoretiro = getUsoretiroFromRequest();
        }
        if (usoretiro == null) {
            usoretiro = new Usoretiro();
        }
        return usoretiro;
    }

    /**
     * Función para asignar el atributo usoretiros y se pueda
     * utilizar en la lista de Uso/Retiro.
     * @return String
     */
    public String listSetup() {
        reset(true);
        usoretiros = getUsoretiros(false);
        return "usoretiro_list";
    }
    /**
     * Función para asignar el atributo usoretiro y se pueda
     * utilizar en la creación de un nuevo Uso/Retiro.
     * @return String
     */
    /*    public String createSetup() {
    reset(false);
    usoretiro = new Usoretiro();
    
    return "usoretiro_create";
    }*/


    /**
     * Getter del IdOperador
     * @return idOperador
     */
    public String getIdOperador() {
        return idOperador;
    }

    /**
     * Función para crear y preparar el atributo usoretiro
     * @return String
     */
    public String crearSetup() {
        reset(false);
        usoretiro = new Usoretiro(this.getItemMax());
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
        Usuario usuarioLogeado = (Usuario) session.getAttribute("user");
        Reserva idReserva = (Reserva) this.getIdReservaFromRequest();
        usoretiro.setIdOperador(usuarioLogeado.getIdUsuario());
        usoretiro.setIdReserva(idReserva);
        usoretiro.setFechaUso(new Date());
        usoretiro.setHoraUso(new Date());
        return "usoretiro_create";
    }

    /**
     * Función que Almacena (crea) la Operación de Uso/Retiro Representada.
     * (Guardar Usoretiro).
     * @return String
     */
    public String create() {
        if (usoretiro.getDevolucionCollection() == null) {
            usoretiro.setDevolucionCollection(new ArrayList<Devolucion>());
        }
        EntityManager em = getEntityManager();
        try {
            utx.begin();
            //Recupera los atributos de la sesion
            HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);

            Reserva idReserva = usoretiro.getIdReserva();
            if (idReserva != null) {
                idReserva = em.getReference(idReserva.getClass(), idReserva.getIdReserva());
                usoretiro.setIdReserva(idReserva);
            }
            Ubicacion idUbicacion = usoretiro.getIdUbicacion();
            if (idUbicacion != null) {
                idUbicacion = em.getReference(idUbicacion.getClass(), idUbicacion.getIdUbicacion());
                usoretiro.setIdUbicacion(idUbicacion);
            }
            List<Devolucion> attachedDevolucionCollection = new ArrayList<Devolucion>();
            for (Devolucion devolucionCollectionDevolucionToAttach : usoretiro.getDevolucionCollection()) {
                devolucionCollectionDevolucionToAttach = em.getReference(devolucionCollectionDevolucionToAttach.getClass(), devolucionCollectionDevolucionToAttach.getIdDevolucion());
                attachedDevolucionCollection.add(devolucionCollectionDevolucionToAttach);
            }
            usoretiro.setDevolucionCollection(attachedDevolucionCollection);
            //Para la audioria, que dispara el trigger de auditoria
            Usuario usuarioLogeado = (Usuario) session.getAttribute("user");
            usoretiro.setUserAudita(usuarioLogeado.getIdUsuario());
            em.persist(usoretiro);
            if (idReserva != null) {
                idReserva.getUsoretiroCollection().add(usoretiro);
                idReserva = em.merge(idReserva);
            }
            if (idUbicacion != null) {
                idUbicacion.getUsoretiroCollection().add(usoretiro);
                idUbicacion = em.merge(idUbicacion);
            }
            for (Devolucion devolucionCollectionDevolucion : usoretiro.getDevolucionCollection()) {
                Usoretiro oldIdUsoRetiroOfDevolucionCollectionDevolucion = devolucionCollectionDevolucion.getIdUsoRetiro();
                devolucionCollectionDevolucion.setIdUsoRetiro(usoretiro);
                devolucionCollectionDevolucion = em.merge(devolucionCollectionDevolucion);
                if (oldIdUsoRetiroOfDevolucionCollectionDevolucion != null) {
                    oldIdUsoRetiroOfDevolucionCollectionDevolucion.getDevolucionCollection().remove(devolucionCollectionDevolucion);
                    oldIdUsoRetiroOfDevolucionCollectionDevolucion = em.merge(oldIdUsoRetiroOfDevolucionCollectionDevolucion);
                }
            }
            utx.commit();
            addSuccessMessage("Uso/retiro fue creado exitosamente.");
        } catch (Exception ex) {
            try {
                if (findUsoretiro(usoretiro.getIdUsoRetiro()) != null) {
                    addErrorMessage("Uso/retiro " + usoretiro + " ya existe.");
                } else {
                    ensureAddErrorMessage(ex, "Ocurrio un error de perstistencia.");
                }
                utx.rollback();
            } catch (Exception e) {
                ensureAddErrorMessage(e, "Ocurrio un error intentando hacer rollback de la transaccion.");
            }
            return null;
        } finally {
            em.close();
        }
        try {//Codigo para hacer un refresh de la lista de Reservas y que solo muestre las que esten pendiente de USO/RETIRO

            HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
            ReservaController controlador = (ReservaController) session.getAttribute("reserva_list");
            controlador.reset(true);
            controlador.setReservas(controlador.getReservas(false));
        } catch (Exception e) {
            ensureAddErrorMessage(e, "Error al intentar refrescar la lista de Reservas");
        }
        //return listSetup();
        return "reserva_list";
    }

    /**
     * Función que preparar los atributos para el jsp de detalles del Usoretiro
     * @return String
     */
    public String detailSetup() {
        return scalarSetup("usoretiro_detail");
    }

    /*    public String editSetup() {
    return scalarSetup("usoretiro_edit");
    }*/
    private String scalarSetup(String destination) {
        reset(false);
        usoretiro = getUsoretiroFromRequest();
        if (usoretiro == null) {
            String requestUsoretiroString = getRequestParameter("jsfcrud.currentUsoretiro");
            addErrorMessage("The usoretiro with id " + requestUsoretiroString + " no longer exists.");
            String relatedControllerOutcome = relatedControllerOutcome();
            if (relatedControllerOutcome != null) {
                return relatedControllerOutcome;
            }
            return listSetup();
        }
        return destination;
    }

    /*
     * Getter del Usoretiro del Request
     */
    private Usoretiro getUsoretiroFromRequest() {
        String theId = getRequestParameter("jsfcrud.currentUsoretiro");
        return (Usoretiro) new UsoretiroConverter().getAsObject(FacesContext.getCurrentInstance(), null, theId);
    }

    /**
     * Función para Obtener el ID-Reserva
     * @return
     */
    private Reserva getIdReservaFromRequest() {
        String theId = getRequestParameter("miidreserva");
        return (Reserva) new ReservaConverter().getAsObject(FacesContext.getCurrentInstance(), null, theId);
    }

    /*
     * Getter de Parametros del Request
     */
    private String getRequestParameter(String key) {
        return FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(key);
    }

    /**
     * Getter de todos los Usoretiros de la Base de Datos (Wrapper)
     * @return List<Usoretiro>
     */
    public List<Usoretiro> getUsoretiros() {
        //if (usoretiros == null) {
        usoretiros = getUsoretiros(false);
        //}
        return usoretiros;
    }

    /**
     * Getter de todos los Usoretiros directamente de la Base de Datos
     * @param all
     * @return q.getResultList()
     */
    public List<Usoretiro> getUsoretiros(boolean all) {
        EntityManager em = getEntityManager();
        try {
            Query q = em.createQuery("select object(o) from Usoretiro as o where o.idReserva.idestadoreserva=2");
            return q.getResultList();
        } finally {
            em.close();
        }
    }

    /*
     *-------------------------------------------------------------------
     * Funciones de manejo de Mensajes y Errores.
     *-------------------------------------------------------------------
     */
    // Asegura que se almacenen los errores
    private void ensureAddErrorMessage(Exception ex, String defaultMsg) {
        String msg = ex.getLocalizedMessage();
        if (msg != null && msg.length() > 0) {
            addErrorMessage(msg);
        } else {
            addErrorMessage(defaultMsg);
        }
    }

    /**
     * Agrega mensajes de Error para la Página.
     * @param msg
     */
    public static void addErrorMessage(String msg) {
        FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg);
        FacesContext.getCurrentInstance().addMessage(null, facesMsg);
    }

    /**
     * Agrega mensajes de Exito para la Página.
     * @param msg
     */
    public static void addSuccessMessage(String msg) {
        FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_INFO, msg, msg);
        FacesContext.getCurrentInstance().addMessage("successInfo", facesMsg);
    }

    /**
     * Obtiene un Usoretiro con el id correspondiente.
     * @param id
     * @return Usoretiro
     */
    public Usoretiro findUsoretiro(Integer id) {
        EntityManager em = getEntityManager();
        try {
            Usoretiro o = (Usoretiro) em.find(Usoretiro.class, id);
            return o;
        } finally {
            em.close();
        }
    }

    /**
     * Función para obtener la Cantidad de UsoRetiros en la base de datos
     * @return int
     */
    public int getItemCount() {
        if (itemCount == -1) {
            EntityManager em = getEntityManager();
            try {
                itemCount = ((Long) em.createQuery("select count(o) from Usoretiro as o").getSingleResult()).intValue();
            } finally {
                em.close();
            }
        }
        return itemCount;
    }

    /**
     * Función para generar el siguiente UsoRetiro
     * @return int
     */
    private int getItemMax() {
        if (itemCount == -1) {
            EntityManager em = getEntityManager();
            try {
                itemCount = ((Long) em.createQuery("SELECT count(o) FROM Usoretiro as o").getSingleResult()).intValue();
                if (itemCount > 0) {
                    Usoretiro d = (Usoretiro) em.createQuery("SELECT s FROM Usoretiro as s order by s.idUsoRetiro DESC").getResultList().get(0);
                    itemCount = d.getIdUsoRetiro();
                }
            } finally {
                em.close();
            }
        }
        return itemCount + 1;
    }

    /*    public int getFirstItem() {
    getItemCount();
    if (firstItem >= itemCount) {
    if (itemCount == 0) {
    firstItem = 0;
    } else {
    int zeroBasedItemCount = itemCount - 1;
    double pageDouble = zeroBasedItemCount / batchSize;
    int page = (int) Math.floor(pageDouble);
    firstItem = page * batchSize;
    }
    }
    return firstItem;
    }
    
    public int getLastItem() {
    getFirstItem();
    return firstItem + batchSize > itemCount ? itemCount : firstItem + batchSize;
    }
    
    public int getBatchSize() {
    return batchSize;
    }
    
    public String next() {
    reset(false);
    getFirstItem();
    if (firstItem + batchSize < itemCount) {
    firstItem += batchSize;
    }
    return "usoretiro_list";
    }
    
    public String prev() {
    reset(false);
    getFirstItem();
    firstItem -= batchSize;
    if (firstItem < 0) {
    firstItem = 0;
    }
    return "usoretiro_list";
    }*/
    private String relatedControllerOutcome() {
        String relatedControllerString = getRequestParameter("jsfcrud.relatedController");
        String relatedControllerTypeString = getRequestParameter("jsfcrud.relatedControllerType");
        if (relatedControllerString != null && relatedControllerTypeString != null) {
            FacesContext context = FacesContext.getCurrentInstance();
            Object relatedController = context.getApplication().getELResolver().getValue(context.getELContext(), null, relatedControllerString);
            try {
                Class<?> relatedControllerType = Class.forName(relatedControllerTypeString);
                Method detailSetupMethod = relatedControllerType.getMethod("detailSetup");
                return (String) detailSetupMethod.invoke(relatedController);
            } catch (ClassNotFoundException e) {
                throw new FacesException(e);
            } catch (NoSuchMethodException e) {
                throw new FacesException(e);
            } catch (IllegalAccessException e) {
                throw new FacesException(e);
            } catch (InvocationTargetException e) {
                throw new FacesException(e);
            }
        }
        return null;
    }

    /**
     * Función para resetear ciertos atributos (usoretiro, usoretiros, 
     * itemCount) de la clase.
     * @param resetFirstItem
     */
    public void reset(boolean resetFirstItem) {
        usoretiro = null;
        usoretiros = null;
        itemCount = -1;
    }
    // atributo para la siguiente función
    private Map<Object, String> asString = null;

    /**
     * Función para Transformar el Usoretiro representado a una forma que pueda
     * ser transmitida en los jsps.
     * @return Map<Object, String>
     */
    public Map<Object, String> getAsString() {
        if (asString == null) {
            asString = new HashMap<Object, String>() {

                @Override
                public String get(Object key) {
                    if (key instanceof Object[]) {
                        Object[] keyAsArray = (Object[]) key;
                        if (keyAsArray.length == 0) {
                            return "(No Items)";
                        }
                        StringBuffer sb = new StringBuffer();
                        for (int i = 0; i < keyAsArray.length; i++) {
                            if (i > 0) {
                                sb.append("<br />");
                            }
                            sb.append(keyAsArray[i]);
                        }
                        return sb.toString();
                    }
                    return new UsoretiroConverter().getAsString(FacesContext.getCurrentInstance(), null, (Usoretiro) key);
                }
            };
        }
        return asString;
    }

    /**
     * Función que Vincula y Valida JSP con el Uso/Retiro Representado.
     * @param facesContext
     * @param component
     * @param value
     */
    public void validateCreate(FacesContext facesContext, UIComponent component, Object value) {
        UsoretiroConverter converter = new UsoretiroConverter();
        String newUsoretiroString = converter.getAsString(FacesContext.getCurrentInstance(), null, new Usoretiro());
        String usoretiroString = converter.getAsString(FacesContext.getCurrentInstance(), null, usoretiro);
        if (!newUsoretiroString.equals(usoretiroString)) {
            crearSetup();
        }
    }

}
